`timescale 1ns/1ps

`define TEST_FILE_PRFIX "E:/develop/FRV232Platform/tb/riscvtest/"

module tb_core;

bit clk;
bit rst_n;

bit                   pd_rst          ;
bit                   pd_wr           ;
bit [31:0]            pd_wdata        ;
bit [8:0]             pd_waddr        ;
bit                   plic_int_vld    ;
bit                   clic_int_vld    ;
wire [5:0]    dev_awtid       ;
wire [31:0]               dev_awaddr      ;
wire [2:0]                dev_awprot      ;
wire                      dev_awvalid     ; 
wire                      dev_awready     ;
wire [5:0]    dev_wtid        ; 
wire [31:0]               dev_wdata       ;
wire [3:0]                dev_wstrb       ;
wire                      dev_wvalid      ;
wire                      dev_wready      ;
wire [5:0]    dev_btid        ;
wire [1:0]                dev_bresp       ;
wire                      dev_bvalid      ;
wire                      dev_bready      ;
wire [5:0]    dev_artid       ;
wire [31:0]               dev_araddr      ;
wire [2:0]                dev_arprot      ;
wire                      dev_arvalid     ;
wire                      dev_arready     ;
wire [5:0]    dev_rtid        ;
wire [31:0]               dev_rdata       ;
wire [1:0]                dev_rresp       ;
wire                      dev_rvalid      ;
wire                      dev_rready      ;    


assign                    dev_awready     = 0;
assign                      dev_wready      = 0;
assign                dev_btid        = 0;
assign                dev_bresp       = 0;
assign                dev_bvalid      = 0;
assign                dev_arready     = 0;
assign                dev_rtid        = 0;
assign                dev_rdata       = 0;
assign                dev_rresp       = 0;
assign                      dev_rvalid      = 0;


frv_core_top _frv_core_top(
.clk             (clk         ),
.rst_n           (rst_n       ),
.pd_rst          (pd_rst      ),
.pd_wr           (pd_wr       ),
.pd_wdata        (pd_wdata    ),
.pd_waddr        (pd_waddr    ),
.plic_int_vld    (plic_int_vld),
.clic_int_vld    (clic_int_vld),
.dev_awtid       (dev_awtid),
.dev_awaddr      (dev_awaddr  ),
.dev_awprot      (dev_awprot  ),
.dev_awvalid     (dev_awvalid ), 
.dev_awready     (dev_awready ),
.dev_wtid        (dev_wtid    ),
.dev_wdata       (dev_wdata   ),
.dev_wstrb       (dev_wstrb   ),
.dev_wvalid      (dev_wvalid  ),
.dev_wready      (dev_wready  ),
.dev_btid        (dev_btid    ),
.dev_bresp       (dev_bresp   ),
.dev_bvalid      (dev_bvalid  ),
.dev_bready      (dev_bready  ),
.dev_artid       (dev_artid   ),
.dev_araddr      (dev_araddr  ),
.dev_arprot      (dev_arprot  ),
.dev_arvalid     (dev_arvalid ),
.dev_arready     (dev_arready ),
.dev_rtid        (dev_rtid    ),
.dev_rdata       (dev_rdata   ),
.dev_rresp       (dev_rresp   ),
.dev_rvalid      (dev_rvalid  ),
.dev_rready      (dev_rready  ) 
);


wire[31:0] registers[31:1];
wire       store_req;
wire[2:0]  store_width;
wire[31:0] store_addr;
wire[31:0] store_data;

// wire[31:0] dmem_data;
wire[3:0]  dmem_strb;


// wire [31:0] bdmem [(2**(16)-1):0];
// wire[31:0] debug_pc;
// logic[31:0] registers_last[31:1];
assign registers = _frv_core_top._frv_isu_top._frv_regfile.regs;

assign store_req  = _frv_core_top._frv_lsu_top.dmem_wren;
assign store_addr = _frv_core_top._frv_lsu_top.dmem_addr;
// assign dmem_data  = _frv_core_top._frv_lsu_top.dmem_wdata;
assign dmem_strb  = _frv_core_top._frv_lsu_top.dmem_strb;

assign store_width= dmem_strb[0] + dmem_strb[1] + dmem_strb[2] + dmem_strb[3];
assign store_data = _frv_core_top._frv_lsu_top.dmem_store_wdata;

// assign debug_pc = _frv_core_top.pc_mem;
// assign bdmem = _frv_core_top._frv_bdram_32.mem;
task unit_test;
input [64*8-1:0] test_name;
integer i,j;
integer fd,sfd;
integer ret;
integer sret;

integer timeout_cnt;

integer line_cnt;

reg[31:0] registers_last[31:1];

string next_event;
reg[31:0] next_value;
reg[31:0] next_pc;

reg[31:0] next_stpc;
reg[31:0] next_staddr;
reg[31:0] next_stdata;
reg[2:0]  next_stwidth;

begin
    rst_n<=1'b0;
    #41 rst_n<=1'b1;

    $readmemh({`TEST_FILE_PRFIX,test_name,".bin.data"},_frv_core_top._frv_bdram_32.mem);
    fd  = $fopen({`TEST_FILE_PRFIX,test_name,".ans"},"r");
    sfd = $fopen({`TEST_FILE_PRFIX,test_name,".sttr"},"r");
    if(fd == 0 || sfd == 0) begin
        $display("Failed to open answer file for %s", test_name);
        return;
    end    
    $display("Running test %0s", test_name);
    registers_last = registers;
    ret = $fscanf(fd, "%x%s%x",next_pc, next_event, next_value); 
    timeout_cnt = 50;
    line_cnt = 0;

    while(ret == 3) begin

      @ (negedge clk);
      if(timeout_cnt == 0) begin
            $display("time out error debug line count=%0d pc=%0x",line_cnt, _frv_core_top._frv_ifu_top.imem_addr);
            $finish;
      end
      timeout_cnt = timeout_cnt - 1;
    //   $display("debug pc=%0x",_frv_core_top._frv_ifu_top.imem_addr);
      if(store_req) begin
        sret = $fscanf(sfd, "%x%x%d%x",next_stpc,next_staddr, next_stwidth, next_stdata);       
        if(next_staddr != store_addr || next_stwidth!= store_width || next_stdata != store_data) begin
            $display("FATAL :Store Error ref_pc=%0x store addr = %x store data = %x store width = %x", next_stpc, next_staddr, next_stdata , next_stwidth);
            $display("FATAL :Store Error rtl store addr = %x store data = %x store width = %x", store_addr, store_data , store_width);
            $finish;
        end
        else begin
            $display("INFO :Store Correct rtl store addr = %x store data = %x store strobe = %b", store_addr, store_data , dmem_strb);     
        end
      end
      if(registers != registers_last) begin
        timeout_cnt = 50;
        line_cnt = line_cnt + 1;
        for(j=0; j<32; j++) begin
            if(registers[j] != registers_last[j]) begin
                string tmp;
                $display("$%0d=%x",j, registers[j]);
                $sformat(tmp, "$%0d", j);
                if(next_event.compare(tmp)==0 &&
                    next_value == registers[j]) begin
                    $display("INFOR ref_pc=%0x correct",next_pc);
                end else begin
                    $display("FATAL :Regfile Error line =%0d ref_pc=%0x should be %s=%x",line_cnt, next_pc, next_event, next_value);
                    $finish;
                end
            end
        end
        registers_last = registers;
        ret = $fscanf(fd, "%x%s%x",next_pc, next_event, next_value);   
        // $display("ret =%d",ret);
      end
    end
    $display("TEST PASS: %0s",test_name);
    $fclose(fd);
end

endtask


task unit_test_no_check;
input [64*8-1:0] test_name;

integer i,j;

integer sret;

integer timeout_cnt;
integer program_cnt;

integer line_cnt;

reg[31:0] registers_last[31:1];


begin
    program_cnt = 100000;
    rst_n<=1'b0;
    #41 rst_n<=1'b1;

    $readmemh({`TEST_FILE_PRFIX,test_name,".data"},_frv_core_top._frv_bdram_32.mem);
    $display("No Check Running test %0s", test_name);
    registers_last = registers;
    while(program_cnt > 0) begin

      @ (negedge clk);
      if(timeout_cnt == 0) begin
            $display("time out error debug pc=%0x", _frv_core_top._frv_ifu_top.imem_addr);
            $finish;
      end
      timeout_cnt = timeout_cnt - 1;

      program_cnt = program_cnt - 1;

      if(store_req) begin
        $display("INFO :Store Error rtl store addr = %x store data = %x store width = %x", store_addr, store_data , store_width);
      end
      if(program_cnt == 0) begin
        $display("TEST End");     
        $finish;
      end
      if(registers != registers_last) begin
        timeout_cnt = 50;
        line_cnt = line_cnt + 1;
        for(j=0; j<32; j++) begin
            if(registers[j] != registers_last[j]) begin
               $display("$%0d=%x",j, registers[j]);
            end
        end
        registers_last = registers;
      end
    end
    $display("TEST PASS: %0s",test_name);
end
endtask

always #5 clk = ~clk;

initial begin
    rst_n <= 0;
    repeat(5) @(posedge clk);
    rst_n <= 1;
    unit_test("add-riscv32-nemu");
    unit_test("fib-riscv32-nemu");
    unit_test("bit-riscv32-nemu");
    unit_test("bubble-sort-riscv32-nemu");
    // unit_test("bubble-sort-riscv32-nemu");
    // unit_test_no_check("dev_access");
    // unit_test("bit_test");
    $finish;
end


endmodule